home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / program / lowpass.zip / LOWPASS.C next >
C/C++ Source or Header  |  1991-12-06  |  13KB  |  364 lines

  1. /* lowpass.c - WinMain() and dialog procedures for LOWPASS, along with
  2.  *      initialization and support code.
  3.  *
  4.  * LOWPASS is a sample application illustrating how to use the multimedia
  5.  *  file I/O services to read and write RIFF files.
  6.  *
  7.  * LOWPASS runs a simple low-pass filter over an 8-bit-per-sample
  8.  * mono WAVE file.  Note that this program does not copy unknown chunks
  9.  * to the output file.
  10.  *
  11.  *
  12.  *     (C) Copyright Microsoft Corp. 1991.  All rights reserved.
  13.  *
  14.  *     You have a royalty-free right to use, modify, reproduce and 
  15.  *     distribute the Sample Files (and/or any modified version) in 
  16.  *     any way you find useful, provided that you agree that 
  17.  *     Microsoft has no warranty obligations or liability for any 
  18.  *     Sample Application Files which are modified. 
  19.  */
  20.  
  21. #include <windows.h>
  22. #include <mmsystem.h>
  23. #include "lowpass.h"
  24.  
  25.  
  26. /* Globals
  27.  */
  28. char        gszAppName[] = "LowPass";   // for title bar, etc.
  29. HANDLE      ghInst;                     // app's instance handle
  30.  
  31.  
  32. /* DoLowPass - Gets the name of the input and output WAVE files from
  33.  *  the dialog box; reads waveform data from the input file, performs
  34.  *  a simple low-pass filter by averaging adjacent samples, and writes
  35.  *  the filtered waveform data to the output WAVE file.
  36.  *
  37.  * Params:  hWnd - Window handle for our dialog box.
  38.  *
  39.  * Returns: 
  40.  */
  41. void DoLowPass(HWND hWnd)
  42. {
  43.     char        achInFile[200];     // name of input file
  44.     char        achOutFile[200];    // name of output file
  45.     HMMIO       hmmioIn = NULL;     // handle to open input WAVE file
  46.     HMMIO       hmmioOut = NULL;    // handle to open output WAVE file
  47.     MMCKINFO    ckInRIFF;           // chunk info. for input RIFF chunk
  48.     MMCKINFO    ckOutRIFF;          // chunk info. for output RIFF chunk
  49.     MMCKINFO    ckIn;               // info. for a chunk in input file
  50.     MMCKINFO    ckOut;              // info. for a chunk in output file
  51.     PCMWAVEFORMAT   pcmWaveFormat;  // contents of 'fmt' chunks
  52.     MMIOINFO    mmioinfoIn;         // current status of <hmmioIn>
  53.     MMIOINFO    mmioinfoOut;        // current status of <hmmioOut>
  54.     long        lSamples;           // number of samples to filter
  55.     BYTE        abSamples[3];       // this, last, and before-last sample
  56.  
  57.     /* Read filenames from dialog box fields.
  58.      */
  59.     achInFile[0] == 0;
  60.     GetDlgItemText(hWnd, ID_INPUTFILEEDIT, achInFile, sizeof(achInFile));
  61.     achOutFile[0] == 0;
  62.     GetDlgItemText(hWnd, ID_OUTPUTFILEEDIT, achOutFile, sizeof(achOutFile));
  63.  
  64.     /* Open the input file for reading using buffered I/O.
  65.      */
  66.     hmmioIn = mmioOpen(achInFile, NULL, MMIO_ALLOCBUF | MMIO_READ);
  67.     if (hmmioIn == NULL)
  68.         goto ERROR_CANNOT_READ;     // cannot open WAVE file
  69.  
  70.     /* Open the output file for writing using buffered I/O. Note that
  71.      * if the file exists, the MMIO_CREATE flag causes it to be truncated
  72.      * to zero length.
  73.      */
  74.     hmmioOut = mmioOpen(achOutFile, NULL,
  75.                         MMIO_ALLOCBUF | MMIO_WRITE | MMIO_CREATE);
  76.     if (hmmioOut == NULL)
  77.         goto ERROR_CANNOT_WRITE;    // cannot open WAVE file
  78.  
  79.     /* Descend the input file into the 'RIFF' chunk.
  80.      */
  81.     if (mmioDescend(hmmioIn, &ckInRIFF, NULL, 0) != 0)
  82.         goto ERROR_CANNOT_READ;     // end-of-file, probably
  83.     
  84.     /* Make sure the input file is a WAVE file.
  85.      */
  86.     if ((ckInRIFF.ckid != FOURCC_RIFF) ||
  87.         (ckInRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
  88.         goto ERROR_BAD_FORMAT;
  89.  
  90.     /* Search the input file for for the 'fmt ' chunk.
  91.      */
  92.     ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
  93.     if (mmioDescend(hmmioIn, &ckIn, &ckInRIFF, MMIO_FINDCHUNK) != 0)
  94.         goto ERROR_BAD_FORMAT;      // no 'fmt ' chunk
  95.     
  96.     /* Expect the 'fmt' chunk to be at least as large as <pcmWaveFormat>;
  97.      * if there are extra parameters at the end, we'll ignore them
  98.      */
  99.     if (ckIn.cksize < (long) sizeof(pcmWaveFormat))
  100.         goto ERROR_BAD_FORMAT;      // 'fmt ' chunk too small
  101.     
  102.     /* Read the 'fmt ' chunk into <pcmWaveFormat>.
  103.      */
  104.     if (mmioRead(hmmioIn, (HPSTR) &pcmWaveFormat,
  105.         (long) sizeof(pcmWaveFormat)) != (long) sizeof(pcmWaveFormat))
  106.         goto ERROR_CANNOT_READ;     // truncated file, probably
  107.  
  108.     /* Ascend the input file out of the 'fmt ' chunk.
  109.      */
  110.     if (mmioAscend(hmmioIn, &ckIn, 0) != 0)
  111.         goto ERROR_CANNOT_READ;     // truncated file, probably
  112.     
  113.     /* Make sure the input file is an 8-bit mono PCM WAVE file.
  114.      */
  115.     if ((pcmWaveFormat.wf.wFormatTag != WAVE_FORMAT_PCM) ||
  116.         (pcmWaveFormat.wf.nChannels != 1) ||
  117.         (pcmWaveFormat.wBitsPerSample != 8))
  118.         goto ERROR_BAD_FORMAT;      // bad input file format
  119.     
  120.     /* Search the input file for for the 'data' chunk.
  121.      */
  122.     ckIn.ckid = mmioFOURCC('d', 'a', 't', 'a');
  123.     if (mmioDescend(hmmioIn, &ckIn, &ckInRIFF, MMIO_FINDCHUNK) != 0)
  124.         goto ERROR_BAD_FORMAT;      // no 'data' chunk
  125.  
  126.     /* Create the output file RIFF chunk of form type 'WAVE'.
  127.      */
  128.     ckOutRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  129.     if (mmioCreateChunk(hmmioOut, &ckOutRIFF, MMIO_CREATERIFF) != 0)
  130.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  131.  
  132.     /* We are now descended into the 'RIFF' chunk we just created.
  133.      * Now create the 'fmt ' chunk. Since we know the size of this chunk,
  134.      * specify it in the MMCKINFO structure so MMIO doesn't have to seek
  135.      * back and set the chunk size after ascending from the chunk.
  136.      */
  137.     ckOut.ckid = mmioFOURCC('f', 'm', 't', ' ');
  138.     ckOut.cksize = sizeof(pcmWaveFormat);   // we know the size of this ck.
  139.     if (mmioCreateChunk(hmmioOut, &ckOut, 0) != 0)
  140.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  141.             
  142.     /* Write the PCMWAVEFORMAT structure to the 'fmt ' chunk.
  143.      */
  144.     if (mmioWrite(hmmioOut, (HPSTR) &pcmWaveFormat, sizeof(pcmWaveFormat))
  145.         != sizeof(pcmWaveFormat))
  146.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  147.     
  148.     /* Ascend out of the 'fmt ' chunk, back into the 'RIFF' chunk.
  149.      */
  150.     if (mmioAscend(hmmioOut, &ckOut, 0) != 0)
  151.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  152.     
  153.     /* Create the 'data' chunk that holds the waveform samples.
  154.      */
  155.     ckOut.ckid = mmioFOURCC('d', 'a', 't', 'a');
  156.     if (mmioCreateChunk(hmmioOut, &ckOut, 0) != 0)
  157.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  158.  
  159.     /* Read samples from the 'data' chunk of the input file, and write
  160.      * samples to the 'data' chunk of the output file.  Each sample in
  161.      * the output file equals the average of the corresponding sample
  162.      * in the input file and the previous two samples from the input file.
  163.      * Access the I/O buffers of <hmmioIn> and <hmmioOut> directly,
  164.      * since this is faster than calling mmioRead() and mmioWrite()
  165.      * for each sample.
  166.      */
  167.     abSamples[0] = abSamples[1] = abSamples[2] = 128;
  168.  
  169.     /* Begin direct access of the I/O buffers.
  170.      */
  171.     if (mmioGetInfo(hmmioIn, &mmioinfoIn, 0) != 0)
  172.         goto ERROR_UNKNOWN;
  173.     if (mmioGetInfo(hmmioOut, &mmioinfoOut, 0) != 0)
  174.         goto ERROR_UNKNOWN;
  175.  
  176.     /* For each input sample, compute and write the output sample.
  177.      */
  178.     for (lSamples = ckIn.cksize; lSamples > 0; lSamples--)
  179.     {
  180.         /* If we are at the end of the input file I/O buffer, fill it.
  181.          * Test to see that we don't hit end of file while (lSamples > 0).
  182.          */
  183.         if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
  184.         {
  185.             if (mmioAdvance(hmmioIn, &mmioinfoIn, MMIO_READ) != 0)
  186.                 goto ERROR_CANNOT_READ;
  187.             
  188.             if (mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)
  189.                 goto ERROR_CANNOT_READ;
  190.         }
  191.  
  192.         /* If we are the end of the output file I/O buffer, flush it.
  193.          */
  194.         if (mmioinfoOut.pchNext == mmioinfoOut.pchEndWrite)
  195.         {
  196.             mmioinfoOut.dwFlags |= MMIO_DIRTY;
  197.             if (mmioAdvance(hmmioOut, &mmioinfoOut, MMIO_WRITE) != 0)
  198.                 goto ERROR_CANNOT_WRITE;
  199.         }
  200.             
  201.         /* Keep track of the last 3 samples so we can average.
  202.          */
  203.         abSamples[2] = abSamples[1];        // next-to-last sample
  204.         abSamples[1] = abSamples[0];        // last sample
  205.         abSamples[0] = *(mmioinfoIn.pchNext)++; // current sample
  206.  
  207.         /* The output file sample is the average of the last
  208.          * 3 input file samples.
  209.          */
  210.         *(mmioinfoOut.pchNext)++ = (BYTE) (((int) abSamples[0]
  211.             + (int) abSamples[1] + (int) abSamples[2]) / 3);
  212.     }
  213.  
  214.     /* We are through processing samples, end direct access of 
  215.      * the I/O buffers. Set the MMIO_DIRTY flag for the output buffer
  216.      * to flush it.
  217.      */
  218.     if (mmioSetInfo(hmmioIn, &mmioinfoIn, 0) != 0)
  219.         goto ERROR_UNKNOWN;
  220.     mmioinfoOut.dwFlags |= MMIO_DIRTY;
  221.     if (mmioSetInfo(hmmioOut, &mmioinfoOut, 0) != 0)
  222.         goto ERROR_CANNOT_WRITE;    // cannot flush, probably
  223.     
  224.     /* Ascend the output file out of the 'data' chunk -- this will cause 
  225.      * the chunk size of the 'data' chunk to be written.
  226.      */
  227.     if (mmioAscend(hmmioOut, &ckOut, 0) != 0)
  228.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  229.  
  230.     /* Ascend the output file out of the 'RIFF' chunk -- this will cause
  231.      * the chunk size of the 'RIFF' chunk to be written.
  232.      */
  233.     if (mmioAscend(hmmioOut, &ckOutRIFF, 0) != 0)
  234.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  235.  
  236.     /* We are done -- files are closed below.
  237.      */
  238.     goto EXIT_FUNCTION;
  239.  
  240. ERROR_BAD_FORMAT:
  241.  
  242.     MessageBox(NULL, "Input file must be an 8-bit mono PCM WAVE file",
  243.         gszAppName, MB_ICONEXCLAMATION | MB_OK);
  244.     goto EXIT_FUNCTION;
  245.  
  246. ERROR_CANNOT_READ:
  247.  
  248.     MessageBox(NULL, "Cannot read input WAVE file",
  249.         gszAppName, MB_ICONEXCLAMATION | MB_OK);
  250.     goto EXIT_FUNCTION;
  251.  
  252. ERROR_CANNOT_WRITE:
  253.  
  254.     MessageBox(NULL, "Cannot write output WAVE file",
  255.         gszAppName, MB_ICONEXCLAMATION | MB_OK);
  256.     goto EXIT_FUNCTION;
  257.  
  258. ERROR_UNKNOWN:
  259.  
  260.     MessageBox(NULL, "Unknown error",
  261.         gszAppName, MB_ICONEXCLAMATION | MB_OK);
  262.     goto EXIT_FUNCTION;
  263.  
  264. EXIT_FUNCTION:
  265.  
  266.     /* Close the files (unless they weren't opened successfully).
  267.      */
  268.     if (hmmioIn != NULL)
  269.         mmioClose(hmmioIn, 0);
  270.     if (hmmioOut != NULL)
  271.         mmioClose(hmmioOut, 0);
  272. }
  273.  
  274.  
  275. /* WinMain - Entry point for LowPass.
  276.  */
  277. int PASCAL WinMain(HANDLE hInst, HANDLE hPrev, LPSTR lpszCmdLine, int iCmdShow)
  278. {
  279.     FARPROC     fpfn;
  280.  
  281.     /* Save instance handle for dialog boxes.
  282.      */
  283.     ghInst = hInst;
  284.  
  285.     /* Display our dialog box.
  286.      */
  287.     fpfn = MakeProcInstance((FARPROC) LowPassDlgProc, ghInst);
  288.     DialogBox(ghInst, "LOWPASSBOX", NULL, (DLGPROC)fpfn);
  289.     FreeProcInstance(fpfn);
  290.  
  291.     return TRUE;
  292. }
  293.  
  294.  
  295. /* AboutDlgProc - Dialog procedure function for ABOUTBOX dialog box.
  296.  */
  297. BOOL FAR PASCAL AboutDlgProc(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
  298. {
  299.     switch (wMsg)
  300.     {
  301.     case WM_INITDIALOG:
  302.         return TRUE;
  303.  
  304.     case WM_COMMAND:
  305.         if (wParam == IDOK)
  306.             EndDialog(hWnd, TRUE);
  307.         break;
  308.     }
  309.     return FALSE;
  310. }
  311.  
  312.  
  313. /* LowPassDlgProc - Dialog procedure function for LOWPASSBOX dialog box.
  314.  */
  315. BOOL FAR PASCAL LowPassDlgProc(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
  316. {
  317.     FARPROC     fpfn;
  318.     HMENU       hmenuSystem;    // system menu
  319.     HCURSOR     ghcurSave;      // previous cursor
  320.  
  321.     switch (wMsg)
  322.     {
  323.     case WM_INITDIALOG:
  324.         /* Append "About" menu item to system menu.
  325.          */
  326.         hmenuSystem = GetSystemMenu(hWnd, FALSE);
  327.         AppendMenu(hmenuSystem, MF_SEPARATOR, 0, NULL);
  328.         AppendMenu(hmenuSystem, MF_STRING, IDM_ABOUT,
  329.             "&About LowPass...");
  330.         return TRUE;
  331.  
  332.     case WM_SYSCOMMAND:
  333.         switch (wParam)
  334.         {
  335.         case IDM_ABOUT:
  336.             /* Display "About" dialog box.
  337.              */
  338.             fpfn = MakeProcInstance((FARPROC) AboutDlgProc, ghInst);
  339.             DialogBox(ghInst, "ABOUTBOX", hWnd, (DLGPROC)fpfn);
  340.             FreeProcInstance(fpfn);
  341.             break;
  342.         }
  343.         break;
  344.  
  345.     case WM_COMMAND:
  346.         switch (wParam)
  347.         {
  348.         case IDOK:          // "Begin"
  349.             /* Set "busy" cursor, filter input file, restore cursor.
  350.              */
  351.             ghcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));
  352.             DoLowPass(hWnd);
  353.             SetCursor(ghcurSave);
  354.             break;
  355.  
  356.         case IDCANCEL:      // "Done"
  357.             EndDialog(hWnd, TRUE);
  358.             break;
  359.         }
  360.         break;
  361.     }
  362.     return FALSE;
  363. }
  364.